/*  Copyright 2012 Bosch Mickaël - Brousset Mathias - Tellier Léo - Thomas Clément
 *
 *  This file is part of shared-drawing.
 * 
 *  shared-drawing is free software: you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free Software
 *  Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 *  shared-drawing is distributed in the hope that it will be useful, but WITHOUT
 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 *  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License along with
 *  shared-drawing. If not, see http://www.gnu.org/licenses/.
 */

package server;

import SharedDrawing.Action;
import common.XmlConstants;
import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jdom2.Document;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

/** @class WorkGroupThread
 *  @brief This is the thread of a WorkGroup
 * 
 */
public class WorkGroupThread extends Thread {
    
    private String hash; //Where is Pikachu ?
        
    private HashMap<Integer, ClientThread> listClients;
    private Integer nb_clients;

    private Integer id_counter;
    
    private Integer page_ids;
    private Integer drawing_ids;
    private Integer wg_content_ids;
    private boolean interrupted;
    private BlockingQueue<Document> in_queue;
    
    /**
     * @brief Add the targeted client to the WorkGroup
     * @param t the ClientThread target
     * @param nick The nickname of the Client
     * @return An identificator for the exchanges, -1 if it fails
     */
    synchronized public Integer addClient(ClientThread t, String nick) {
        ClientThread tmp;
        synchronized (this) {
            for (Integer i : listClients.keySet())
            {
                tmp = listClients.get(i);
                if (tmp.getName().equals(t.getName())) {
                    Action action = new Action(XmlConstants.error);
                    action.addContent(XmlConstants.message,
                        "another client is already " + tmp.getName());
                    t.send(action);
                    System.out.println(
                            "can't add, already exists");
                    return -1;
                }
            }
            nb_clients++;
            id_counter++;
            listClients.put(id_counter-1, t);
        }
        return id_counter-1;
    }

    /**
     * Add a Document to the Queue
     * @param doc The Document to add
     * @throws InterruptedException
     */
    synchronized public void addToQueue(Document doc) throws InterruptedException
    {
        in_queue.put(doc);
    }
    
    /**
     * Delete a Client in the Workgroup
     * @param t The thread of the Client
     */
    synchronized public void delClient(ClientThread t) {
        synchronized (this) {
            System.out.println("[Workgroup] Goodbye " + t.getName() + " !");
            listClients.remove(t.getUserId());
            nb_clients--;
            if(nb_clients == 0)
            {
                interrupted = true;
                interrupt();
            }
        }
    }
    
    /**
     * Get the number of Client in the Workgroup
     * @return The number of Client
     */
    synchronized public double getNbClients() {
        return nb_clients;
    }
    
    /**
     * Get the hash of the Password
     * @return A String of the hash
     */
    synchronized public String getHash() {
        return hash;
    }
    
    /**
     * Broadcast a message to the Clients
     * @param msg The message to broadcast
     */
    synchronized private void broadcasting(Document to_send)
    {
        XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
        String msg = outputter.outputString(to_send);
        System.out.println("[Workgroup] " + getName() + " broadcasting to " + listClients.size() + " clients.");
        System.out.println(msg);
        ClientThread tmp;
        synchronized (this) {
            for (Integer id : listClients.keySet()) // parcours de la table des connectÃ©s
            {
                tmp = listClients.get(id);
                if (tmp != null) {
                    try {
                        if(tmp.getUpToDate())
                        {
                            tmp.send(msg);
                        }      
                        else
                        {
                            System.out.println("it is buffered for : " + tmp.getUserId());
                            tmp.bufferize(to_send);
                        }
                    } catch (IOException ex) {
                        //the client quit
                        tmp.dieNow();
                    }
                }
            }
        }
    }
    
    /**
     * Get a ClientThread by its id
     * @param id The id of the Client
     * @return A ClientThread
     */
    public ClientThread getClient(Integer id)
    {
        return listClients.get(id);
    }

    WorkGroupThread(String theName) {
        this(theName, null);
    }
    
    WorkGroupThread(String theName, String theHash) {
        hash = theHash;
        this.setName(theName);
        
        nb_clients = 0;
        id_counter = 0;
    
        page_ids = 0;
        drawing_ids = 0;
        wg_content_ids = 0;
        
        listClients = new HashMap();
        interrupted = false;
        in_queue = new LinkedBlockingQueue();
    }

    /**
     * The main of the WorkGroupThread
     */
    @Override
    public void run() {
        System.out.println("[Workgroup] " + getName() + " now exists.");
        while(!interrupted)
        {
            Document doc;
            try {
                doc = in_queue.take();
                broadcasting(doc);
            } catch (InterruptedException ex) {/*there is no more client*/}
            
        }
        Server.getServer().delWorkgroup(this);
        System.out.println("[Workgroup] " + getName() + " doesn't exist anymore.");
    }

    /**
     * Incremental id giver for the drawings
     * @return A String of the id
     */
    synchronized public String getNextDrawingId() {
        drawing_ids++;
        return drawing_ids.toString();
    }

    /**
     * Incremental id giver of the pages
     * @return A String of the id
     */
    synchronized public String getNextPageId() {
        page_ids++;
        return page_ids.toString();
    }
    
    /**
     * Incremental id giver of the ShapeContent
     * @return A String of the id
     */
    synchronized public String getNextShapeContentId() {
        wg_content_ids++;
        return wg_content_ids.toString();
    }

    /**
     * Ask the updates for a Client
     * @param client The asking ClientThread
     */
    synchronized public void askUpdate(ClientThread client) {
        boolean failed = true;
        for(Integer id : listClients.keySet())
        {
            System.out.println("workgroup : trying client " + id);

            if((listClients.get(id) != client) && (listClients.get(id) != null))
            {
                if(listClients.get(id).askUpdate(client.getUserId()) == true)
                {
                    System.out.println("asking done");
                    failed = false;
                    break;
                }
            }
            else
            {
                System.out.println("workgroup : it is me or i am null");
            }
        }
        if(failed)
        {
            System.out.println("workgroup : can't make up to date !");
            client.makeUpToDate();
        }
    }
    
}
